package fun.easycode.webterminal.common.datafill;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import fun.easycode.webterminal.common.util.CamelUnderUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Component
public class MyBatisPlusStrategy implements FillFieldStrategy{

    @Override
    @SuppressWarnings("all")
    public void fill(Class<?> source,String sourceKey, List<Metadata> metadataList) {

        BaseMapper mapper = (BaseMapper) SpringUtil.getBean(source);
        if(mapper == null){
            log.error("mapper bean is null {}", ClassUtil.getClassName(source, false));
            return;
        }

        // 要查询的id集合
        Set idList = new HashSet();

        // 非属性填充的对象集合，后面查询出来填充值会用
        List<Object> notPropertyList = new ArrayList<>();

        // 要查询的列集合
        Set<String> queryColumnList = new HashSet<>();

        for(Metadata metadata : metadataList){
            String queryPkFieldName = metadata.getQueryPk();

            if(!StringUtils.hasLength(queryPkFieldName)){
                log.error("queryPk field name is empty {} {}.", ClassUtil.getClassName(metadata.getTarget(), false)
                        , metadata.getTargetField().getName());
                continue;
            }

            if(FillType.ObjectFill == metadata.getType()){
                Object targetFieldValue = ReflectUtil.getFieldValue(metadata.getTarget(), metadata.getTargetField());

                Object primaryKeyValue = ReflectUtil.getFieldValue(targetFieldValue, queryPkFieldName);

                if(primaryKeyValue == null){
                    log.error("queryPk field value is null {} {} .", ClassUtil.getClassName(metadata.getTarget(), false)
                            , metadata.getTargetField().getName());
                    continue;
                }

                idList.add(primaryKeyValue);
                notPropertyList.add(targetFieldValue);

                Arrays.stream(ReflectUtil.getFields(targetFieldValue.getClass())).forEach(field -> {
                    queryColumnList.add(field.getName());
                });

            }else {
                Object primaryKeyValue = ReflectUtil.getFieldValue(metadata.getTarget(), queryPkFieldName);
                if(primaryKeyValue == null){
                    log.error("queryPk field value is null {} {} .", ClassUtil.getClassName(metadata.getTarget(), false)
                            , queryPkFieldName);
                    continue;
                }
                idList.add(primaryKeyValue);
                queryColumnList.add(metadata.getSource().sourceProperty());
            }
        }


        queryColumnList.add(CamelUnderUtil.camel2under(sourceKey));

        QueryWrapper wrapper = new QueryWrapper();
        wrapper.select(queryColumnList.toArray());

        wrapper.in(CamelUnderUtil.camel2under(sourceKey), idList);

        List<Object> resultList = mapper.selectList(wrapper);

        if(resultList.isEmpty()){
            log.warn("result list is empty {}", metadataList);
            return;
        }

        Class sourceDataClass = resultList.get(0).getClass();

        Field sourceKeyField = ReflectUtil.getField(sourceDataClass, sourceKey);

        if(sourceKeyField == null){
            log.error("source key field is null {}", sourceDataClass);
            return;
        }

        Map<Object,Object> idObjMap = resultList.stream()
                .collect(Collectors.toMap(obj -> ReflectUtil.getFieldValue(obj, sourceKeyField), Function.identity()));

        for(Metadata metadata : metadataList){
            if(FillType.PropertyFill == metadata.getType()){
                Object r = idObjMap.get(ReflectUtil.getFieldValue(metadata.getTarget(), metadata.getQueryPk()));
                if(r == null){
                    continue;
                }
                String fieldName = metadata.getSource().sourceProperty();

                if(!StringUtils.hasLength(fieldName)){
                    log.error("field name is null {}", metadata);
                    continue;
                }

                Object fieldValue = ReflectUtil.getFieldValue(r, fieldName);
                ReflectUtil.setFieldValue(metadata.getTarget(), metadata.getTargetField(), fieldValue);
            }else {
                for(Object obj : notPropertyList){
                    Object queryKeyValue = ReflectUtil.getFieldValue(obj, metadata.getQueryPk());
                    Object r = idObjMap.get(queryKeyValue);
                    if(r == null){
                        continue;
                    }
                    BeanUtil.copyProperties(r, obj);
                }
            }
        }

    }

    @Override
    public Class<?> getType() {
        return BaseMapper.class;
    }
}
